package httpstunnel

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"github.com/urfave/cli"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"time"
)

var (
	clientConn net.Conn
	serverAddr string
	localAddr  string
	domain     string
	localHost  string
	localPort  string
)

func RunClient(ctx *cli.Context) error {

	serverAddr = ctx.String("s")
	localAddr = ctx.String("l")
	domain = ctx.String("domain")

	localHost, localPort, _ = net.SplitHostPort(localAddr)

	if serverAddr == "" || localAddr == "" || domain == "" {
		log.Fatal("invalid args")
	}
	var err error
	clientConn, err = net.Dial("tcp", serverAddr)

	if err != nil {
		return err
	}

	_, err = clientConn.Write(tcpWriteWrap(CmdDeRegistry, []byte(domain)))

	if err != nil {
		return err
	}

	go readConn()
	writeConn()
	return nil
}

func writeConn() {
	tk := time.NewTicker(30 * time.Second)
	for {
		select {
		case <-tk.C:
			clientConn.Write(tcpWriteWrap(CmdPing, []byte("ping")))
		}
	}
}

func readConn() {
	for {
		h := make([]byte, 2)
		_, err := clientConn.Read(h)
		if err != nil {
			log.Println("[error]", err)
			return
		}
		l := binary.BigEndian.Uint16(h)
		cmd := make([]byte, l)
		_, err = clientConn.Read(cmd)
		if err != nil {
			log.Println("[error]", err)
			return
		}

		var b2 = make([]byte, 2)
		_, err = clientConn.Read(b2)
		if err != nil {
			log.Println("[error]", err)
			return
		}

		bl := binary.BigEndian.Uint16(b2)
		b2 = make([]byte, bl)
		_, err = clientConn.Read(b2)
		if err != nil {
			log.Println("[error]", err)
			return
		}

		switch string(cmd) {
		case CmdPong:
		case CmdProxy:
			handleClientProxy(b2)
		default:
			log.Println("unknown command: ", string(cmd))
		}

	}
}

func handleClientProxy(body []byte) {

	idx := body[len(body)-4:]

	req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(body[:len(body)-4])))
	if err != nil {
		log.Println("[ERROR] ", err)
		return
	}

	req.Host = localAddr
	req.URL.Host = localAddr

	resp, err := http.DefaultClient.Do(req)
	defer resp.Body.Close()

	b, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		log.Println("[ERROR] ", err)
		return
	}

	b = append(b, idx...)
	clientConn.Write(tcpWriteWrap(CmdProxy, b))
}
